package com.warski.wonderfires.dlna.player;

import android.content.*;
import android.os.IBinder;
import android.util.Log;

import com.warski.wonderfires.dlna.listener.DLANDeviceConnectListener;
import com.warski.wonderfires.dlna.listener.DLNAControlListener;
import com.warski.wonderfires.dlna.model.DeviceInfo;
import com.warski.wonderfires.dlna.model.MediaInfo;
import com.warski.wonderfires.dlna.service.DLNABrowserService;
import com.warski.wonderfires.dlna.type.EnumDeviceStatus;
import com.warski.wonderfires.dlna.type.EnumServiceType;
import com.warski.wonderfires.utils.Logger;
import org.fourthline.cling.android.AndroidUpnpService;
import org.fourthline.cling.controlpoint.ActionCallback;
import org.fourthline.cling.model.action.ActionArgumentValue;
import org.fourthline.cling.model.action.ActionInvocation;
import org.fourthline.cling.model.message.UpnpResponse;
import org.fourthline.cling.model.meta.Service;
import org.fourthline.cling.support.avtransport.callback.*;
import org.fourthline.cling.support.model.DIDLObject;
import org.fourthline.cling.support.model.PositionInfo;
import org.fourthline.cling.support.model.ProtocolInfo;
import org.fourthline.cling.support.model.Res;
import org.fourthline.cling.support.model.item.AudioItem;
import org.fourthline.cling.support.model.item.ImageItem;
import org.fourthline.cling.support.model.item.VideoItem;
import org.fourthline.cling.support.renderingcontrol.callback.GetVolume;
import org.fourthline.cling.support.renderingcontrol.callback.SetMute;
import org.fourthline.cling.support.renderingcontrol.callback.SetVolume;
import org.seamless.util.MimeType;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import static com.warski.wonderfires.dlna.DLNAManager.dlnaPlayer;

/**
 * Created by xuhaiping on 2020/9/2.
 */
public class DLNAPlayer {
    private DeviceInfo currentDeviceInfo;
    private DLANDeviceConnectListener currentConnectListener;
    private MediaInfo currentMediaInfo;
    private Context context;
    private ServiceConnection serviceConnection;
    private AndroidUpnpService upnpService;

    public DLNAPlayer(Context context) {
        this.context = context;
        this.serviceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                upnpService = (AndroidUpnpService) service;
                if (currentDeviceInfo != null) {
                    currentDeviceInfo.setState(EnumDeviceStatus.CONNECTED);
                }
                if (currentConnectListener != null) {
                    currentConnectListener.onConnected();
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                if (currentDeviceInfo != null) {
                    currentDeviceInfo.setState(EnumDeviceStatus.DISCONNECTED);
                }
            }
        };
    }

    public void release() {
        disconnect();
    }

    public void connect(DeviceInfo deviceInfo, DLANDeviceConnectListener listener) {
        currentDeviceInfo = deviceInfo;
        currentConnectListener = listener;
        context.bindService(new Intent(context, DLNABrowserService.class), serviceConnection, Context.BIND_AUTO_CREATE);
    }

    public void disconnect() {
        if (serviceConnection != null) {
            context.unbindService(serviceConnection);
            serviceConnection = null;
        }
        currentDeviceInfo.setState(EnumDeviceStatus.DISCONNECTED);
        currentDeviceInfo = null;
        currentConnectListener = null;
    }

    public DeviceInfo getCurrentDevice() {
        return currentDeviceInfo;
    }

    private void execute(ActionCallback actionCallback) {
        if (upnpService != null) {
            upnpService.getControlPoint().execute(actionCallback);
        }
    }

    public void play(final DLNAControlListener callback) {
        final Service avtService = currentDeviceInfo.findService(EnumServiceType.AVTransport);
        execute(new Play(avtService) {
            @Override
            public void success(ActionInvocation invocation) {
                super.success(invocation);
                currentDeviceInfo.setState(EnumDeviceStatus.PLAY);
                if (callback != null) {
                    callback.onDLNASuccess(invocation);
                }
            }

            @Override
            public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMsg) {
                currentDeviceInfo.setState(EnumDeviceStatus.ERROR);
                if (callback != null) {
                    callback.onDLNAError(invocation, defaultMsg);
                }
            }
        });
    }

    public void pause(final DLNAControlListener callback) {
        final Service avtService = currentDeviceInfo.findService(EnumServiceType.AVTransport);
        execute(new Pause(avtService) {
            @Override
            public void success(ActionInvocation invocation) {
                super.success(invocation);
                currentDeviceInfo.setState(EnumDeviceStatus.PAUSE);
                if (callback != null) {
                    callback.onDLNASuccess(invocation);
                }
            }

            @Override
            public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMsg) {
                currentDeviceInfo.setState(EnumDeviceStatus.ERROR);
                if (callback != null) {
                    callback.onDLNAError(invocation, defaultMsg);
                }
            }
        });
    }


    public void stop(final DLNAControlListener callback) {
        final Service avtService = currentDeviceInfo.findService(EnumServiceType.AVTransport);
        if (avtService == null) return;
        execute(new Stop(avtService) {
            @Override
            public void success(ActionInvocation invocation) {
                super.success(invocation);
                currentDeviceInfo.setState(EnumDeviceStatus.STOP);
                if (callback != null) {
                    callback.onDLNASuccess(invocation);
                }
            }

            @Override
            public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMsg) {
                currentDeviceInfo.setState(EnumDeviceStatus.ERROR);
                if (callback != null) {
                    callback.onDLNAError(invocation, defaultMsg);
                }
            }
        });
    }

    public void seekTo(String time, final DLNAControlListener callback) {
        final Service avtService = currentDeviceInfo.findService(EnumServiceType.AVTransport);
        execute(new Seek(avtService, time) {
            @Override
            public void success(ActionInvocation invocation) {
                super.success(invocation);
                if (callback != null) {
                    callback.onDLNASuccess(invocation);
                }
            }

            @Override
            public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMsg) {
                currentDeviceInfo.setState(EnumDeviceStatus.ERROR);
                if (callback != null) {
                    callback.onDLNAError(invocation, defaultMsg);
                }
            }
        });
    }

    public void setVolume(long volume, final DLNAControlListener callback) {
        final Service rcService = currentDeviceInfo.findService(EnumServiceType.RenderingControl);
        execute(new SetVolume(rcService, volume) {
            @Override
            public void success(ActionInvocation invocation) {
                super.success(invocation);
                callback.onDLNASuccess(invocation);
            }

            @Override
            public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMsg) {
                currentDeviceInfo.setState(EnumDeviceStatus.ERROR);
                callback.onDLNAError(invocation, defaultMsg);
            }
        });
    }

    public void mute(boolean desiredMute, final DLNAControlListener callback) {
        final Service rcService = currentDeviceInfo.findService(EnumServiceType.RenderingControl);
        execute(new SetMute(rcService, desiredMute) {
            @Override
            public void success(ActionInvocation invocation) {
                super.success(invocation);
                callback.onDLNASuccess(invocation);
            }

            @Override
            public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMsg) {
                currentDeviceInfo.setState(EnumDeviceStatus.ERROR);
                callback.onDLNAError(invocation, defaultMsg);
            }
        });
    }


    public void getPositionInfo(final DLNAControlListener callback) {
        final Service avtService = currentDeviceInfo.findService(EnumServiceType.AVTransport);
        if (avtService == null) return;
        execute(new GetPositionInfo(avtService) {
            @Override
            public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMsg) {
                currentDeviceInfo.setState(EnumDeviceStatus.ERROR);
                if (callback != null) {
                    callback.onDLNAError(invocation, defaultMsg);
                }
            }

            @Override
            public void success(ActionInvocation invocation) {
                super.success(invocation);
                if (callback != null) {
                    callback.onDLNASuccess(invocation);
                }
            }

            @Override
            public void received(ActionInvocation invocation, PositionInfo info) {
                if (callback != null) {
                    callback.onDLNAMessage(invocation, info);
                }
            }
        });
    }


    public void getVolume(final DLNAControlListener callback, boolean isUp) {
        final Service avtService = currentDeviceInfo.findService(EnumServiceType.RenderingControl);
        execute(new GetVolume(avtService) {
            @Override
            public void success(ActionInvocation invocation) {
                super.success(invocation);
//                callback.onDLNASuccess(invocation);
            }
            @Override
            public void received(ActionInvocation invocation, int currentVolume) {
                Log.d("currentKEYCODE","get currentVolume --->" + currentVolume);
                if (isUp) {
                    currentVolume += 5;
                    if (currentVolume > 100) currentVolume = 100;
                    dlnaPlayer.setVolume(currentVolume,null);
                }else {
                    currentVolume -= 5;
                    if (currentVolume < 0) currentVolume = 0;
                    dlnaPlayer.setVolume(currentVolume,null);
                }
                Log.d("currentKEYCODE","set currentVolume ---> " + currentVolume);

//                callback.onDLNAMessage(invocation, currentVolume);
            }

            @Override
            public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMsg) {
                currentDeviceInfo.setState(EnumDeviceStatus.ERROR);
//                callback.onDLNAError(invocation, defaultMsg);
            }
        });
    }

    public void setDataSource(MediaInfo mediaInfo) {
        currentMediaInfo = mediaInfo;
    }

    public void start(final DLNAControlListener callback) {
        if (currentMediaInfo == null) {
            callback.onDLNAError(null, "Missing Data Source");
        }

        String metadata = buildMediaMetadata();
        final Service avtService = currentDeviceInfo.findService(EnumServiceType.AVTransport);
        execute(new SetAVTransportURI(avtService, currentMediaInfo.getUrl(), metadata) {
            @Override
            public void success(ActionInvocation invocation) {
                super.success(invocation);
                play(callback);
            }

            @Override
            public void failure(ActionInvocation invocation, UpnpResponse operation, String defaultMsg) {
                currentDeviceInfo.setState(EnumDeviceStatus.ERROR);
                callback.onDLNAError(invocation, defaultMsg);
            }
        });
    }

    private String buildMediaMetadata() {
        final long size = 0;
        final Res res = new Res(new MimeType(ProtocolInfo.WILDCARD, ProtocolInfo.WILDCARD), size, currentMediaInfo.getUrl());
        final String creator = "wonderfires";
        final String parentId = "0";
        final String metadata;

        switch (currentMediaInfo.getMediaType()) {
            case TYPE_IMAGE:
                ImageItem imageItem = new ImageItem(currentMediaInfo.getMediaId(), parentId, currentMediaInfo.getMediaName(), creator, res);
                metadata = createItemMetadata(imageItem);
                break;
            case TYPE_VIDEO:
                VideoItem videoItem = new VideoItem(currentMediaInfo.getMediaId(), parentId, currentMediaInfo.getMediaName(), creator, res);
                metadata = createItemMetadata(videoItem);
                break;
            case TYPE_AUDIO:
                AudioItem audioItem = new AudioItem(currentMediaInfo.getMediaId(), parentId, currentMediaInfo.getMediaName(), creator, res);
                metadata = createItemMetadata(audioItem);
                break;
            default:
                throw new IllegalArgumentException("UNKNOWN MEDIA TYPE");
        }
        Logger.info("metadata: %s", metadata);
        return metadata;
    }

    private static final String DIDL_LITE_FOOTER = "</DIDL-Lite>";
    private static final String DIDL_LITE_HEADER = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>"
            + "<DIDL-Lite "
            + "xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\" "
            + "xmlns:dc=\"http://purl.org/dc/elements/1.1/\" "
            + "xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\" "
            + "xmlns:dlna=\"urn:schemas-dlna-org:metadata-1-0/\">";

    /**
     * 创建投屏的参数
     *
     * @param item
     * @return
     */
    private String createItemMetadata(DIDLObject item) {
        StringBuilder metadata = new StringBuilder();
        metadata.append(DIDL_LITE_HEADER);

        metadata.append(String.format("<item id=\"%s\" parentID=\"%s\" restricted=\"%s\">", item.getId(), item.getParentID(), item.isRestricted() ? "1" : "0"));

        metadata.append(String.format("<dc:title>%s</dc:title>", item.getTitle()));
        String creator = item.getCreator();
        if (creator != null) {
            creator = creator.replaceAll("<", "_");
            creator = creator.replaceAll(">", "_");
        }
        metadata.append(String.format("<upnp:artist>%s</upnp:artist>", creator));
        metadata.append(String.format("<upnp:class>%s</upnp:class>", item.getClazz().getValue()));

        DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
        Date now = new Date();
        String time = sdf.format(now);
        metadata.append(String.format("<dc:date>%s</dc:date>", time));

        Res res = item.getFirstResource();
        if (res != null) {
            // protocol info
            String protocolinfo = "";
            ProtocolInfo pi = res.getProtocolInfo();
            if (pi != null) {
                protocolinfo = String.format("protocolInfo=\"%s:%s:%s:%s\"", pi.getProtocol(), pi.getNetwork(), pi.getContentFormatMimeType(), pi
                        .getAdditionalInfo());
            }
            Logger.info("protocolinfo: %s", protocolinfo);
            // resolution, extra info, not adding yet
            String resolution = "";
            if (res.getResolution() != null && res.getResolution().length() > 0) {
                resolution = String.format("resolution=\"%s\"", res.getResolution());
            }
            // duration
            String duration = "";
            if (res.getDuration() != null && res.getDuration().length() > 0) {
                duration = String.format("duration=\"%s\"", res.getDuration());
            }
            // res begin
            //            metadata.append(String.format("<res %s>", protocolinfo)); // no resolution & duration yet
            metadata.append(String.format("<res %s %s %s>", protocolinfo, resolution, duration));
            // url
            String url = res.getValue();
            metadata.append(url);
            // res end
            metadata.append("</res>");
        }
        metadata.append("</item>");
        metadata.append(DIDL_LITE_FOOTER);
        return metadata.toString();
    }
}
